package CoroutineTool

import (
	"context"
	"errors"
	"sync"
	"time"
)

type contextCancel struct {
	Context    context.Context
	CancelFunc context.CancelFunc
	UnixNano   int64
}

type contextCancelArrLock struct {
	lock        sync.RWMutex
	contextsArr map[string]contextCancel
}

var contextsArrLock contextCancelArrLock

func init() {
	// 初始化全局变量
	contextsArrLock.contextsArr = make(map[string]contextCancel)
	contextsTreeLock.contextsArr = make(map[string]contextCancel)
}

func SetContextCancel(key string) error {
	contextsArrLock.lock.Lock()
	defer contextsArrLock.lock.Unlock()
	if contextsArrLock.contextsArr[key].UnixNano > 0 {
		return errors.New("this key in map!")
	}
	ctx, cancel := context.WithCancel(context.Background())
	var tempContexts contextCancel
	unixNano := time.Now().UnixNano()
	tempContexts.Context = ctx
	tempContexts.CancelFunc = cancel
	tempContexts.UnixNano = unixNano
	contextsArrLock.contextsArr[key] = tempContexts
	return nil
}

func GetContextCancel(key string) context.Context {
	contextsArrLock.lock.RLock()
	defer contextsArrLock.lock.RUnlock()
	if contextsArrLock.contextsArr[key].UnixNano <= 0 {
		return nil
	}
	return contextsArrLock.contextsArr[key].Context
}

func GetAllContextCancelKey() []string {
	contextsArrLock.lock.RLock()
	defer contextsArrLock.lock.RUnlock()
	arr := []string{}
	for key, _ := range contextsArrLock.contextsArr {
		arr = append(arr, key)
	}
	return arr
}
func RunCancel(key string) error {
	contextsArrLock.lock.Lock()
	defer contextsArrLock.lock.Unlock()
	if contextsArrLock.contextsArr[key].UnixNano <= 0 {
		return errors.New("this key not in map!")
	}
	contextsArrLock.contextsArr[key].CancelFunc()
	delete(contextsArrLock.contextsArr, key)
	return nil
}

// TreeNode represents a node in the tree

type ContextTreeNode struct {
	Value    string
	Children []*ContextTreeNode
	lock     sync.RWMutex
}

// AddChild adds a child node to the current node
func (n *ContextTreeNode) addChild(child *ContextTreeNode) {
	n.lock.Lock()
	defer n.lock.Unlock()
	n.Children = append(n.Children, child)
}

// GetAllChildren returns a slice containing all child nodes of the current node
func (n *ContextTreeNode) getAllChildren() []*ContextTreeNode {
	n.lock.RLock()
	defer n.lock.RUnlock()
	var returnContextTreeNode []*ContextTreeNode
	//递归获取所有子孙节点
	return recursionChildren(n, returnContextTreeNode, true)

}

func recursionChildren(n *ContextTreeNode, returnContextTreeNode []*ContextTreeNode, isFirst bool) []*ContextTreeNode {
	if !isFirst {
		returnContextTreeNode = append(returnContextTreeNode, n)
	}
	for _, child := range n.Children {
		returnContextTreeNode = recursionChildren(child, returnContextTreeNode, false)
	}
	return returnContextTreeNode
}

// FindNode recursively finds a node with a specific value
func (n *ContextTreeNode) findNode(value string) *ContextTreeNode {
	n.lock.RLock()
	defer n.lock.RUnlock()
	if value == "" {
		return nil
	}
	if n.Value == value {
		return n
	}
	for _, child := range n.Children {
		if found := child.findNode(value); found != nil {
			return found
		}
	}
	return nil
}

// DeleteNode deletes a node with a specific value and its children
func deleteNode(value string) (bool, error) {
	node := rootNode.findNode(value)
	if node == nil {
		return false, errors.New("this key not in tree!")
	}
	node.lock.Lock()
	defer node.lock.Unlock()
	node.Children = nil
	node.Value = ""
	return true, nil
}

var contextsTreeLock contextCancelArrLock
var contextsTreeRootName = "<!?=contextsTreeRoot=?!>"

var rootNode = ContextTreeNode{
	Value:    contextsTreeRootName,
	Children: []*ContextTreeNode{},
}

func SetTreeContextCancel(key string, fatherKey string) error {
	contextsTreeLock.lock.Lock()
	defer contextsTreeLock.lock.Unlock()
	if rootNode.findNode(key) != nil {
		return errors.New("this key in tree!")
	}
	var node = rootNode.findNode(fatherKey)
	if node == nil && fatherKey != "" {
		return errors.New("this fatherKey not in tree,your fatherKey can set \"\",this is root!")
	}

	ctx, cancel := context.WithCancel(context.Background())
	var tempContexts contextCancel
	unixNano := time.Now().UnixNano()
	tempContexts.Context = ctx
	tempContexts.CancelFunc = cancel
	tempContexts.UnixNano = unixNano

	if node == nil {
		rootNode.addChild(&ContextTreeNode{
			Value:    key,
			Children: []*ContextTreeNode{},
		})
	} else {
		node.addChild(&ContextTreeNode{
			Value:    key,
			Children: []*ContextTreeNode{},
		})
	}
	contextsTreeLock.contextsArr[key] = tempContexts
	return nil
}

func GetTreeContextCancel(key string) context.Context {
	contextsTreeLock.lock.RLock()
	defer contextsTreeLock.lock.RUnlock()
	if contextsTreeLock.contextsArr[key].UnixNano <= 0 {
		return nil
	}
	return contextsTreeLock.contextsArr[key].Context
}
func GetTreeAllContextCancelKey() []string {
	contextsTreeLock.lock.RLock()
	defer contextsTreeLock.lock.RUnlock()
	arr := []string{}
	for _, treeNode := range rootNode.getAllChildren() {
		arr = append(arr, treeNode.Value)
	}
	return arr
}
func GetTreeChildrenContextCancelKey(key string) []string {
	contextsTreeLock.lock.RLock()
	defer contextsTreeLock.lock.RUnlock()
	thisNode := rootNode.findNode(key)
	if thisNode == nil {
		return nil
	}
	var arr []string
	for _, treeNode := range thisNode.getAllChildren() {
		arr = append(arr, treeNode.Value)
	}
	return arr

}
func RunTreeCancel(key string) (bool, error) {
	contextsTreeLock.lock.Lock()
	defer contextsTreeLock.lock.Unlock()
	thisNode := rootNode.findNode(key)
	if key == contextsTreeRootName {
		return false, errors.New("this key not is sys.root!")
	}
	if thisNode == nil {
		return false, errors.New("this key not in tree!")
	}
	for _, treeNode := range thisNode.getAllChildren() {
		cancel := contextsTreeLock.contextsArr[treeNode.Value]
		cancel.CancelFunc()
		delete(contextsTreeLock.contextsArr, treeNode.Value)
	}
	cancel := contextsTreeLock.contextsArr[key]
	cancel.CancelFunc()
	delete(contextsTreeLock.contextsArr, key)
	return deleteNode(key)

}

/*
func main() {

	SetTreeContextCancel("root", "")
	time.Sleep(time.Second * 1)
	go func() {
		SetTreeContextCancel("root1", "root")
		ctx1 := GetTreeContextCancel("root1")
		fmt.Println("yi-")
		go func() {
			SetTreeContextCancel("root2", "root1")
			ctx2 := GetTreeContextCancel("root2")
			fmt.Println("er-")
			go func() {
				SetTreeContextCancel("root3", "root2")
				ctx3 := GetTreeContextCancel("root3")
				fmt.Println("shan-")
				go func() {
					SetTreeContextCancel("root4", "root3")
					ctx4 := GetTreeContextCancel("root4")
					fmt.Println("si-")
					select {
					case <-ctx4.Done():
						fmt.Printf("4")
					}
				}()
				select {
				case <-ctx3.Done():
					fmt.Printf("3")
				}
			}()
			select {
			case <-ctx2.Done():
				fmt.Printf("2")
			}
		}()
		select {
		case <-ctx1.Done():
			fmt.Printf("1")
		}
	}()
	go func() {
		SetTreeContextCancel("root5", "root")
		ctx5 := GetTreeContextCancel("root5")
		fmt.Println("wu-")
		select {
		case <-ctx5.Done():
			fmt.Printf("5")
		}
	}()

	time.Sleep(time.Second * 1)

	RunTreeCancel("root")
	time.Sleep(time.Second * 2)
	fmt.Println(GetTreeAllContextCancelKey())
}
*/
